package im.actor.crypto.primitives.curve25519;

public class ge_scalarmult_base {

//CONVERT #include "ge.h"
//CONVERT #include "crypto_uint32.h"

static int equal(byte b,byte c)
{
  int ub = b;
  int uc = c;
  int x = ub ^ uc; /* 0: yes; 1..255: no */
  int y = x; /* 0: yes; 1..255: no */
  y -= 1; /* 4294967295: yes; 0..254: no */
  y >>>= 31; /* 1: yes; 0: no */
  return y;
}

static int negative(byte b)
{
  long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
  x >>>= 63; /* 1: yes; 0: no */
  return (int)x;
}

static void cmov(ge_precomp t,ge_precomp u,int b)
{
  fe_cmov.fe_cmov(t.yplusx,u.yplusx,b);
  fe_cmov.fe_cmov(t.yminusx,u.yminusx,b);
  fe_cmov.fe_cmov(t.xy2d,u.xy2d,b);
}

static void select(ge_precomp t,int pos,byte b)
{
  ge_precomp base[][] =  (pos <= 7 ? ge_precomp_base_0_7.base :
                           (pos <= 15 ? ge_precomp_base_8_15.base :
                             (pos <= 23 ? ge_precomp_base_16_23.base : ge_precomp_base_24_31.base)));

  ge_precomp minust = new ge_precomp();
  int bnegative = negative(b);
  int babs = b - (((-bnegative) & b) << 1);

  ge_precomp_0.ge_precomp_0(t);
  cmov(t,base[pos][0],equal((byte)babs,(byte)1));
  cmov(t,base[pos][1],equal((byte)babs,(byte)2));
  cmov(t,base[pos][2],equal((byte)babs,(byte)3));
  cmov(t,base[pos][3],equal((byte)babs,(byte)4));
  cmov(t,base[pos][4],equal((byte)babs,(byte)5));
  cmov(t,base[pos][5],equal((byte)babs,(byte)6));
  cmov(t,base[pos][6],equal((byte)babs,(byte)7));
  cmov(t,base[pos][7],equal((byte)babs,(byte)8));
  fe_copy.fe_copy(minust.yplusx,t.yminusx);
  fe_copy.fe_copy(minust.yminusx,t.yplusx);
  fe_neg.fe_neg(minust.xy2d,t.xy2d);
  cmov(t,minust,bnegative);
}

/*
h = a * B
where a = a[0]+256*a[1]+...+256^31 a[31]
B is the Ed25519 base point (x,4/5) with x positive.

Preconditions:
  a[31] <= 127
*/

public static void ge_scalarmult_base(ge_p3 h,byte[] a)
{
  byte[] e = new byte[64];
  byte carry;
  ge_p1p1 r = new ge_p1p1();
  ge_p2 s = new ge_p2();
  ge_precomp t = new ge_precomp();
  int i;

  for (i = 0;i < 32;++i) {
    e[2 * i + 0] = (byte)((a[i] >>> 0) & 15);
    e[2 * i + 1] = (byte)((a[i] >>> 4) & 15);
  }
  /* each e[i] is between 0 and 15 */
  /* e[63] is between 0 and 7 */

  carry = 0;
  for (i = 0;i < 63;++i) {
    e[i] += carry;
    carry = (byte)(e[i] + 8);
    carry >>= 4;
    e[i] -= carry << 4;
  }
  e[63] += carry;
  /* each e[i] is between -8 and 8 */

  ge_p3_0.ge_p3_0(h);
  for (i = 1;i < 64;i += 2) {
    select(t,i / 2,e[i]);
    ge_madd.ge_madd(r,h,t); ge_p1p1_to_p3.ge_p1p1_to_p3(h,r);
  }

  ge_p3_dbl.ge_p3_dbl(r,h); ge_p1p1_to_p2.ge_p1p1_to_p2(s,r);
  ge_p2_dbl.ge_p2_dbl(r,s); ge_p1p1_to_p2.ge_p1p1_to_p2(s,r);
  ge_p2_dbl.ge_p2_dbl(r,s); ge_p1p1_to_p2.ge_p1p1_to_p2(s,r);
  ge_p2_dbl.ge_p2_dbl(r,s); ge_p1p1_to_p3.ge_p1p1_to_p3(h,r);

  for (i = 0;i < 64;i += 2) {
    select(t,i / 2,e[i]);
    ge_madd.ge_madd(r,h,t); ge_p1p1_to_p3.ge_p1p1_to_p3(h,r);
  }
}

}
